﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using Vibz.Common.Channel;
using Vibz.Game.Bugs.Model;
using System.Threading;
using Vibz.Common.Sequential;
using System.Media;
using System.IO;
using Vibz.Gauge;
using Vibz.Common;
using Vibz.Common.Model;


namespace Vibz.Game.Bugs
{
    public partial class GameWindow : ClientTerminal
    {
        PlayerInstance playerMe;
        GroundObjectInstanceList gInstList;
        delegate void UpdateControllCallback<T>(T control, object arg);

        SelectablePanel panelGround;
        Gauge.Gauge gaugeSpeed; 
        GaugeRange gaugeRange1 = new GaugeRange();
        GaugeRange gaugeRange2 = new GaugeRange();
        GaugeRange gaugeRange3 = new GaugeRange();
        GaugeRange gaugeRange4 = new GaugeRange();

        void CustomInitializeComponent()
        {
            InitializeComponent();
            this.gaugeSpeed = new Vibz.Gauge.Gauge();
            this.panelGround = new Vibz.Game.Bugs.SelectablePanel();
            // 
            // gaugeSpeed
            // 
            //this.gaugeSpeed.Anchor = 
            //    ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.gaugeSpeed.Dock = DockStyle.Top;
            this.gaugeSpeed.BaseArcColor = System.Drawing.Color.Gray;
            this.gaugeSpeed.BaseArcRadius = 80;
            this.gaugeSpeed.BaseArcStart = 135;
            this.gaugeSpeed.BaseArcSweep = 270;
            this.gaugeSpeed.BaseArcWidth = 2;
            this.gaugeSpeed.Center = new System.Drawing.Point(100, 100);
            gaugeRange1.Color = System.Drawing.Color.Lime;
            gaugeRange1.EndValue = 75F;
            gaugeRange1.InnerRadius = 65;
            gaugeRange1.InRange = false;
            gaugeRange1.Name = "GaugeRange1";
            gaugeRange1.OuterRadius = 80;
            gaugeRange1.StartValue = 25F;
            gaugeRange2.Color = System.Drawing.Color.LightCoral;
            gaugeRange2.EndValue = 100F;
            gaugeRange2.InnerRadius = 65;
            gaugeRange2.InRange = false;
            gaugeRange2.Name = "GaugeRange2";
            gaugeRange2.OuterRadius = 80;
            gaugeRange2.StartValue = 75F;
            gaugeRange3.Color = System.Drawing.Color.LightCoral;
            gaugeRange3.EndValue = 25F;
            gaugeRange3.InnerRadius = 65;
            gaugeRange3.InRange = false;
            gaugeRange3.Name = "GaugeRange3";
            gaugeRange3.OuterRadius = 80;
            gaugeRange3.StartValue = 0F;
            gaugeRange4.Color = System.Drawing.Color.MidnightBlue;
            gaugeRange4.EndValue = 100F;
            gaugeRange4.InnerRadius = 50;
            gaugeRange4.InRange = false;
            gaugeRange4.Name = "GaugeRange4";
            gaugeRange4.OuterRadius = 53;
            gaugeRange4.StartValue = 0F;
            this.gaugeSpeed.GaugeRanges.Add(gaugeRange1);
            this.gaugeSpeed.GaugeRanges.Add(gaugeRange2);
            this.gaugeSpeed.GaugeRanges.Add(gaugeRange3);
            this.gaugeSpeed.GaugeRanges.Add(gaugeRange4);
            this.gaugeSpeed.MaxValue = 100F;
            this.gaugeSpeed.MinValue = 0F;
            this.gaugeSpeed.Name = "gaugeSpeed";
            this.gaugeSpeed.NeedleColor1 = Vibz.Gauge.GaugeNeedleColor.Gray;
            this.gaugeSpeed.NeedleColor2 = System.Drawing.Color.DarkSlateGray;
            this.gaugeSpeed.NeedleRadius = 80;
            this.gaugeSpeed.NeedleType = Vibz.Gauge.NeedleType.Advance;
            this.gaugeSpeed.NeedleWidth = 3;
            this.gaugeSpeed.ScaleLinesInterColor = System.Drawing.Color.Black;
            this.gaugeSpeed.ScaleLinesInterInnerRadius = 73;
            this.gaugeSpeed.ScaleLinesInterOuterRadius = 80;
            this.gaugeSpeed.ScaleLinesInterWidth = 1;
            this.gaugeSpeed.ScaleLinesMajorColor = System.Drawing.Color.Black;
            this.gaugeSpeed.ScaleLinesMajorInnerRadius = 60;
            this.gaugeSpeed.ScaleLinesMajorOuterRadius = 80;
            this.gaugeSpeed.ScaleLinesMajorStepValue = 10F;
            this.gaugeSpeed.ScaleLinesMajorWidth = 2;
            this.gaugeSpeed.ScaleLinesMinorColor = System.Drawing.Color.Gray;
            this.gaugeSpeed.ScaleLinesMinorInnerRadius = 75;
            this.gaugeSpeed.ScaleLinesMinorOuterRadius = 80;
            this.gaugeSpeed.ScaleLinesMinorTicks = 10;
            this.gaugeSpeed.ScaleLinesMinorWidth = 1;
            this.gaugeSpeed.ScaleNumbersColor = System.Drawing.Color.Black;
            this.gaugeSpeed.ScaleNumbersFormat = null;
            this.gaugeSpeed.ScaleNumbersRadius = 95;
            this.gaugeSpeed.ScaleNumbersRotation = 0;
            this.gaugeSpeed.ScaleNumbersStartScaleLine = 0;
            this.gaugeSpeed.ScaleNumbersStepScaleLines = 10;
            this.gaugeSpeed.Size = new System.Drawing.Size(205, 180);
            this.gaugeSpeed.TabIndex = 8;
            this.gaugeSpeed.Value = 0F;
            // 
            // panelGround
            // 
            //this.panelGround.Dock = DockStyle.Fill;
            this.panelGround.BorderStyle = BorderStyle.FixedSingle;
            this.panelGround.Anchor = AnchorStyles.None;
            this.panelGround.Location = new System.Drawing.Point(0, 0);
            this.panelGround.Name = "panelGround";
            this.panelGround.Size = new System.Drawing.Size(315, 325);
            this.panelGround.TabIndex = 7;
            this.panelGround.TabStop = true;
            this.panelGround.BackColor = Color.White;


            this.splitContainer2.Panel1.Controls.Add(this.panelGround);
            this.splitContainer2.Panel2.Controls.Add(this.gaugeSpeed);

            splitContainer2.Panel1.BackColor = Color.White;
            //this.Controls.Add(this.panelGround);
            //this.Controls.Add(this.gaugeSpeed);
        }
        
        public GameWindow()
        {
            try
            {
                //TODO: This is required. Check how this can be removed
                Model = new Ground();
                this.MinimumSize = new Size(900, 700);

                CustomInitializeComponent();

                gInstList = new GroundObjectInstanceList();
                panelGround.KeyDown += new KeyEventHandler(pnlGround_KeyDown);
                lvPlayers.BackColor = Color.DarkGray;
                lvPlayers.ListViewItemSorter = new ListViewItemComparer();
                this.splitContainer2.Panel2MinSize = 200;
                this.splitContainer1.Panel2MinSize = 50;

                SetStyle(ControlStyles.UserPaint, true);
                SetStyle(ControlStyles.AllPaintingInWmPaint, true);
                SetStyle(ControlStyles.DoubleBuffer, true);
                SetStyle(ControlStyles.UserPaint, true);
                SetStyle(ControlStyles.AllPaintingInWmPaint, true);
                SetStyle(ControlStyles.DoubleBuffer, true);
            }
            catch (Exception exc)
            { 
                Log.Write(exc); 
            }
        }
        
        public override void Start()
        {
            Log.Write("Game Begins.");

            PictureBox pb = new PictureBox();
            pb.Size = new System.Drawing.Size(200, 200);
            pb.Image = (Image)Vibz.Game.Bugs.Properties.Resources.loading;
            pb.Location = new Point((this.panelGround.Size.Width / 2) - 50, (this.panelGround.Size.Height / 2) - 50);
            panelGround.Controls.Add(pb);
        }
        
        void UpdateControlData<T>(T control, object arg)
        {
            if ((control as Control).InvokeRequired)
            {
                UpdateControllCallback<T> d = new UpdateControllCallback<T>(UpdateControlData);
                this.Invoke(d, new object[] { control, arg });
            }
            else
            {
                string method = ((object[])arg).GetValue(0).ToString();
                switch (method)
                {
                    case "AddListView":
                        ListViewItem lvi = (ListViewItem)((object[])arg).GetValue(1);
                        (control as ListView).Items.Add(lvi);
                        break;
                    case "SortListView":
                        (control as ListView).Sort();
                        break;
                    case "InitPanel":
                        Ground gnd = Model as Ground;
                        (control as Panel).Controls.Clear();
                        (control as Panel).MaximumSize = new Size(gnd.Width, gnd.Height);
                        (control as Panel).MinimumSize = new Size(gnd.Width, gnd.Height);
                        (control as Panel).Location = new Point((panelGround.Width - gnd.Width) / 2, (panelGround.Height - gnd.Height) / 2);
                        (control as Panel).Paint += new PaintEventHandler(GameWindow_Paint);
                        (control as Panel).Focus();
                        break;
                }
            }
        }
        
        public override void SynchronizeObjects()
        {
            try
            {
                if (Model != null)
                {
                    Ground gnd = Model as Ground;
                    List<GroundObject> goList = gnd.Objects.Except(gnd.Players).ToList();
                    for (int i = 0; i < goList.Count; i++)
                    {
                        GroundObject go = (GroundObject)goList[i];
                        lock (gInstList)
                        {
                            gInstList.AddUpdate(go);
                        }
                    }
                    for (int i = 0; i < gnd.Players.Count; i++)
                    {
                        Player p = gnd.Players[i];
                        if (playerMe != null && playerMe.Data.Equals(p))
                        {
                            playerMe.Data.Score = p.Score;
                        }
                        else
                        {
                            PlayerInstance pi = (PlayerInstance)gInstList.Find(p);
                            if (pi != null)
                            {
                                if (pi.Data.Direction != p.Direction)
                                {
                                    pi.Data = p;
                                    pi.Body = new Rectangle(p.Location, new Size(p.Thickness, p.Thickness));
                                }
                            }
                            else
                            {
                                PlayerInstance gnInst = new PlayerInstance(gInstList, p);
                                lock (gInstList)
                                {
                                    gInstList.Add(gnInst);
                                }
                                ListViewItem lvi = new ListViewItem { Tag = p, ImageIndex = 2 };
                                if (p.EndPoint == this.ClientID)
                                {
                                    playerMe = gnInst;
                                    lvi = new ListViewItem { Tag = p, ImageIndex = 1 };
                                }
                                SetPlayerStats(lvi, p);
                                UpdateControlData<ListView>(lvPlayers, new object[] { "AddListView", lvi });
                            }
                        }
                    }
                    UpdateControlData<ListView>(lvPlayers, new object[] { "SortListView" });
                }
            }
            catch (Exception exc)
            {
                Log.Write(exc);
                throw;
            }
        }

        public override void FirstLoad()
        {
            UpdateControlData<Panel>(panelGround, new object[] { "InitPanel" });
        }
        
        void SetPlayerStats(ListViewItem lvi, Player p)
        {
            lvi.Text = p.ID + "        Score:" + p.Score;
            lvi.Name = p.ID.ToString();
            lvi.Font = new System.Drawing.Font(FontFamily.GenericSerif, 12, FontStyle.Bold);
        }
        
        void GameWindow_Paint(object sender, PaintEventArgs e)
        {
            try
            {
                Ground gnd = Model as Ground;
                if (gnd != null)
                {
                    lock (gInstList)
                    {
                        List<GroundObjectInstance> goiList = gInstList.Except(gInstList.OfType<PlayerInstance>().ToList()).ToList();
                        for (int i = 0; i < goiList.Count; i++)
                        {
                            goiList[i].Display(panelGround, e);
                        }

                        List<PlayerInstance> pil = gInstList.OfType<PlayerInstance>().ToList();
                        for (int i = 0; i < pil.Count; i++)
                        {
                            PlayerInstance pi = pil[i];
                            ListViewItem lvi = null;
                            ListViewItem[] lvis = lvPlayers.Items.Find(pi.Data.ID.ToString(), true);
                            if (lvis.Count() > 0)
                            {
                                lvi = lvis.First();
                                SetPlayerStats(lvi, pi.Data);
                            }
                            if (playerMe != null && playerMe.Data.Equals(pi.Data))
                            {
                                playerMe.Display(panelGround, e);
                                if (lvi != null)
                                    lvi.BackColor = Color.Blue;
                            }
                            else
                            {
                                if (pi != null)
                                {
                                    pi.Display(panelGround, e);
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception exc)
            {
                Log.Write(exc);
                throw;
            }
        }

        void pnlGround_KeyDown(object sender, KeyEventArgs e)
        {
            MoveDirection direction = MoveDirection.NA;
            switch (e.KeyData)
            {
                case Keys.Up:
                    direction = (e.Modifiers == Keys.Control) ? MoveDirection.UpRight : MoveDirection.Up;
                    break;
                case Keys.Down:
                    direction = (e.Modifiers == Keys.Control) ? MoveDirection.DownLeft : MoveDirection.Down;
                    break;
                case Keys.Left:
                    direction = (e.Modifiers == Keys.Control) ? MoveDirection.UpLeft : MoveDirection.Left;
                    break;
                case Keys.Right:
                    direction = (e.Modifiers == Keys.Control) ? MoveDirection.DownRight : MoveDirection.Right;
                    break;
                case Keys.Space:
                    if (playerMe.Data.HasPower(PowerType.DriveDiogonal))
                        direction = playerMe.Data.Direction != MoveDirection.UpLeft ? ++playerMe.Data.Direction : MoveDirection.Up;
                    break;
                case Keys.F2:
                    NotepadHelper.ShowMessage(Log.Read(), "Logs");
                    break;
            }
            if (direction != MoveDirection.NA)
            {
                playerMe.Data.Direction = direction;
                playerMe.Data.Location = playerMe.Body.Location;
                // UpdateServer(new DataPacket<Player>(playerMe.Data, new DataPacketHeader("ChangeDirection")));
            }
        }

        void DetectCollision()
        {
            try
            {
                int PlayerX = playerMe.Data.Location.X;
                int PlayerY = playerMe.Data.Location.Y;

                for (int i = 0; i < gInstList.Count; i++)
                {
                    GroundObjectInstance gInst = gInstList[i];
                    if (!playerMe.Data.Equals(gInst.Data) 
                        && playerMe.Body.IntersectsWith(gInst.Body)
                        && !playerMe.Data.Contains(gInst.Data))
                    {
                        //UpdateServer();
                        HandleCollision(gInst.Data);
                        gInst.Revoke();
                    }
                }
            }
            catch (Exception exc)
            {
                Log.Write(exc);
            }
        }

        void HandleCollision(GroundObject objData)
        {
            //DataPacket retValue = null;
            objData.Merge(playerMe.Data);
            GroundObjectInstance gi = gInstList.Find(objData);
            panelGround.Invalidate(gi.Body);
            //gi.Body = new Rectangle(objData.Location, gi.Body.Size);
            //gi.RequiresRedraw = true;
            if (objData.GetType() == typeof(Power))
            {
                objData.Location = new Point(-100, -100);
                //retValue = new DataPacket<Power>((Power)objData, new DataPacketHeader("Collision"));
                new SoundPlayer(Vibz.Game.Bugs.Properties.Resources.coin).Play();
            }
            else if (objData.GetType() == typeof(Coin))
            {
                objData.Location = new Point(-100, -100);
                //retValue = new DataPacket<Coin>((Coin)objData, new DataPacketHeader("Collision"));
                new SoundPlayer(Vibz.Game.Bugs.Properties.Resources.coin).Play();
            }
            else if (objData.GetType() == typeof(Obstacle))
            {
                objData.Location = new Point(-100, -100);
                //retValue = new DataPacket<Obstacle>((Obstacle)objData, new DataPacketHeader("Collision"));
                new SoundPlayer(Vibz.Game.Bugs.Properties.Resources.obstacle).Play();
            }

            else if (objData.GetType() == typeof(Compartment))
            {
                //retValue = new DataPacket<Compartment>((Compartment)objData, new DataPacketHeader("Collision"));
                new SoundPlayer(Vibz.Game.Bugs.Properties.Resources.compartment).Play();
            }

            //if (gInst.Data.GetType() == typeof(Player)) ;
            //TODO: playerMe.Data.Speed -= 2;

            //return retValue;
        }

        public override void DoPeriodicTask()
        {
            try
            {
                Ground gnd = Model as Ground;
                if (gnd == null || playerMe == null)
                    return;
                lock (gnd)
                {
                    foreach (Player p in gnd.Players)
                    {
                        if (playerMe.Data.ID == p.ID)
                        {
                            playerMe.Move(gnd);
                            DetectCollision();
                        }
                        else
                        {
                            PlayerInstance pi = (PlayerInstance)gInstList.Find(p);
                            if (pi != null)
                                pi.Move(gnd);
                        }
                    }
                    InvalidateGroundObjects();

                    lvPowers.Items.Clear();
                    foreach (PowerType pt in playerMe.Data.PowerDurations.Keys)
                    {
                        string ptText = pt.ToString() + "    " + playerMe.Data.PowerDurations[pt];
                        List<ListViewItem> lvis = lvPowers.Items.Find(pt.ToString(), true).ToList();
                        ListViewItem lvi = new ListViewItem { Text = ptText, Name = pt.ToString() };
                        UpdateControlData<ListView>(lvPowers, new object[] { "AddListView", lvi });
                    }
                    gaugeSpeed.Value = (float)playerMe.GroundSpeed;
                    lblStats.Text = playerMe.Body.Location.ToString();
                    lblScore.Text = "Score: " + playerMe.Data.Score.ToString();
                }
            }
            catch (Exception exc)
            {
                Log.Write(exc);
                throw;
            }
        }

        void InvalidateGroundObjects()
        {
            foreach (GroundObjectInstance gi in gInstList)
            {
                if (gi != null && gi.RequiresRedraw)
                {
                    if (gi.PreviousOrientation.Rectangle != null)
                        panelGround.Invalidate(gi.PreviousOrientation.Rectangle);

                    panelGround.Invalidate(gi.Body);

                    gi.RequiresRedraw = false;
                }
            }
        }

        void GameWindow_Resize(object sender, EventArgs e)
        {
            ResizeSplitters();
        }

        void GameWindow_Load(object sender, EventArgs e)
        {
            ResizeSplitters();
        }

        void ResizeSplitters()
        {
            splitContainer2.SplitterDistance = this.Width - 220;
            splitContainer1.SplitterDistance = this.Height - 50;
        }
    }
}



